<template>
  <div class="pdf">
    <div class="toolbar">
      <el-space>
        <div>
          <el-input-number
            class="current-page"
            size="small"
            :controls="false"
            :model-value="currentPage"
            @change="currentPageChange"
          />&nbsp;/&nbsp;{{ docData?.numPages }}
        </div>
        <el-select
          size="small"
          @change="scaleChange"
          :model-value="Math.round(scale * 100)"
        >
          <el-option
            v-for="item in [50, 100, 110, 120, 200]"
            :key="item"
            :label="`${item}%`"
            :value="item"
          />
        </el-select>
      </el-space>
    </div>
    <div
      class="container"
      ref="containerRef"
      :style="{
        '--scale-factor': scale,
      }"
      @scroll="pdfScroll"
      @wheel="pdfWheel"
    ></div>
  </div>
</template>
<script setup lang="ts">
import * as pdfjsLib from "pdfjs-dist";
import type { PDFPageProxy, PDFDocumentProxy } from "pdfjs-dist";
import { computed, ref } from "vue";
import * as worker from "@/assets/pdf.worker.min.mjs";

pdfjsLib.GlobalWorkerOptions.workerSrc = worker; // 指定worker路径
const PAGE_GAP = computed(() => `${scale.value * 20}px`); // 页码间距

const containerRef = ref<HTMLDivElement>(); // 渲染pdf的容器
const docData = ref<PDFDocumentProxy>(); // pdf文件数据
const currentPage = ref(1); // 当前页码
const pdfPageHeight = ref(1); // 每页高度
const scale = ref(1);

// 读区pdf文件，并逐页渲染
pdfjsLib
  .getDocument(
    "https://api.ydcyapt.com/default/2024/01/10/af25c2c8143e4211af3d9c95d62e6a05.pdf"
  )
  .promise.then((doc) => {
    docData.value = doc;
    for (let i = 1; i <= doc.numPages; i++) {
      doc.getPage(i).then((page) => {
        renderPage(page);
      });
    }
  });
/**
 * 渲染pdf页面
 * @param page pdf页面
 */
const renderPage = (page: PDFPageProxy, scale: number = 1) => {
  if (!containerRef.value) return;
  const canvas = document.createElement("canvas");
  const div = document.createElement("div");
  containerRef.value.appendChild(div);
  div.appendChild(canvas);
  const ctx = canvas.getContext("2d");
  if (ctx) {
    const viewport = page.getViewport({ scale: scale });
    pdfPageHeight.value = viewport.height + parseInt(PAGE_GAP.value);
    canvas.width = viewport.width;
    canvas.height = viewport.height;
    canvas.style.width = `calc(var(--scale-factor) * ${viewport.width}px)`;
    canvas.style.height = `calc(var(--scale-factor) * ${viewport.height}px)`;
    const outputScale = window.devicePixelRatio || 1;
    const transform =
      outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : undefined;
    const renderContext: Parameters<PDFPageProxy["render"]>[0] = {
      canvasContext: ctx,
      viewport: viewport,
      transform,
    };
    page.render(renderContext);
  }
};

/** 滚动条事件 */
const pdfScroll = () => {
  if (containerRef.value) {
    currentPage.value =
      Math.floor(containerRef.value.scrollTop / pdfPageHeight.value) + 1;
  }
};
/** 滚轮事件 */
const pdfWheel = (e: WheelEvent) => {
  if (e.ctrlKey && containerRef.value) {
    e.preventDefault();
    const delta = e.deltaY || e.detail;
    const zoomFactor = 0.03;
    const lastscale = scale.value;
    scale.value = scale.value + (delta > 0 ? -zoomFactor : zoomFactor);
    containerRef.value.scrollTop =
      containerRef.value.scrollTop * (scale.value / lastscale);
  }
};

/**
 * 跳转到指定页码
 * @param value 页码
 */
const currentPageChange = (value?: number) => {
  currentPage.value = value || 1;
  const scrollTopValue =
    (currentPage.value - 1) * pdfPageHeight.value + parseInt(PAGE_GAP.value);
  containerRef.value!.scrollTo({ top: scrollTopValue });
};

const scaleChange = (value: number) => {
  scale.value = value / 100;
};
</script>
<style lang="scss" scoped>
.pdf {
  width: 100%;
  height: 100%;
  overflow: hidden;

  .toolbar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 32px;
    z-index: 9999;
    padding: 0 40px;
    background-color: #f7f7f9;
    border-bottom: 1px solid;
    display: flex;
    align-items: center;
    .current-page {
      width: 50px;
    }
  }

  .container {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: #cacaca;
    padding: 20px 0 20px;
    margin-top: 32px;
    overflow-y: scroll;
    height: calc(100% - 32px);
    & > :not(:last-child) {
      margin-bottom: v-bind(PAGE_GAP);
    }
  }
}
</style>
